﻿using System;
using System.Web;
using System.Web.Mvc;
// ReSharper disable once CheckNamespace
namespace Sharp.Portal.Mvc
{
    /// <summary>
    /// 防止HttpPost重复提交
    /// </summary>
    /// <remarks>http://www.cnblogs.com/wintersun/archive/2012/03/10/2389084.html</remarks>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class ValidateReHttpPostTokenAttribute : FilterAttribute, IAuthorizationFilter
    {
        public IPageTokenView PageTokenView { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidateReHttpPostTokenAttribute"/> class.
        /// </summary>
        public ValidateReHttpPostTokenAttribute()
        {
            //It would be better use DI inject it.
            PageTokenView = new SessionPageTokenView();
        }

        /// <summary>
        /// Called when authorization is required.
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException(nameof(filterContext));
            }

            if (!PageTokenView.TokensMatch)
            {
                //log...
                throw new Exception("Invaild Http Post!");
            }

        }
    }

    public interface IPageTokenView
    {
        /// <summary>
        /// Generates the page token.
        /// </summary>
        string GeneratePageToken();

        /// <summary>
        /// Gets the get last page token from Form
        /// </summary>
        string GetLastPageToken { get; }

        /// <summary>
        /// Gets a value indicating whether [tokens match].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [tokens match]; otherwise, <c>false</c>.
        /// </value>
        bool TokensMatch { get; }
    }

    public abstract class PageTokenViewBase : IPageTokenView
    {
        public static readonly string HiddenTokenName = "hiddenToken";
        public static readonly string SessionMyToken = "Token";

        /// <summary>
        /// Generates the page token.
        /// </summary>
        /// <returns></returns>
        public abstract string GeneratePageToken();

        /// <summary>
        /// Gets the get last page token from Form
        /// </summary>
        public abstract string GetLastPageToken { get; }

        /// <summary>
        /// Gets a value indicating whether [tokens match].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [tokens match]; otherwise, <c>false</c>.
        /// </value>
        public abstract bool TokensMatch { get; }

    }

    public class SessionPageTokenView : PageTokenViewBase
    {
        #region PageTokenViewBase

        /// <summary>
        /// Generates the page token.
        /// </summary>
        /// <returns></returns>
        public override string GeneratePageToken()
        {
            if (HttpContext.Current.Session[SessionMyToken] != null)
            {
                return HttpContext.Current.Session[SessionMyToken].ToString();
            }
            else
            {
                var token = GenerateHashToken();
                HttpContext.Current.Session[SessionMyToken] = token;
                return token;
            }
        }

        /// <summary>
        /// Gets the get last page token from Form
        /// </summary>
        public override string GetLastPageToken
        {
            get { return HttpContext.Current.Request.Params[HiddenTokenName]; }
        }

        /// <summary>
        /// Gets a value indicating whether [tokens match].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [tokens match]; otherwise, <c>false</c>.
        /// </value>
        public override bool TokensMatch
        {
            get
            {
                string formToken = GetLastPageToken;
                if (formToken != null)
                {
                    if (formToken.Equals(GeneratePageToken()))
                    {
                        //Refresh token
                        HttpContext.Current.Session[SessionMyToken] = GenerateHashToken();
                        return true;
                    }
                }
                return false;
            }
        }

        #endregion

        #region Private Help Method

        /// <summary>
        /// Generates the hash token.
        /// </summary>
        /// <returns></returns>
        private string GenerateHashToken()
        {
            return (HttpContext.Current.Session.SessionID + DateTime.Now.Ticks).Md5();
        }

        #endregion
    }
}